<?php


namespace common\service;

use common\model\PlanModel;
use common\model\PlaylistItemVersionModel;
use common\model\ReviewFeedbackModel;
use common\model\ReviewRequestNodeModel;
use common\model\ReviewRequestModel;
use common\model\TaskWorkOrderModel;
use support\EntityTaskFilterParser;
use common\model\StepCategoryModel;
use common\model\ProjectModel;
use common\model\StatusModel;
use common\model\EntityModel;
use common\model\StepModel;
use common\model\TaskModel;
use common\model\UserModel;
use support\SingletonTrait;
use think\module\Module;
use think\Exception;
use support\bootstrap\Redis;
use function request;

class TaskLibraryService
{
    use SingletonTrait;
    use EntityTaskFilterParser;

    protected $taskWorkOrderType = ['task_making'];
    protected $listQueryTaskField = [
        "module.code as module_code",
        'entity.name',
        'entity.code',
        'entity.code as entity_code',
        'entity.initial_episode_code',
        'entity.showings_number',
        'entity.entity_id as entity_parent_id',
        'entity.step_workflow_id',
        'entity.module_id as entity_module_id',
        'entity.json as entity_json',
        'entity.director_star as director_star',
        'entity.key_shot as key_shot',
        'entity.is_append as is_append',
        "entity.step_workflow_id",
        'task.id',
        'task.entity_id',
        'task.name as task_name',
        'task.plan_start_time',
        'task.plan_end_time',
        'task.plan_delivery_time',
        'task.end_time',
        'task.step_id',
        'task.step_type',
        'task.step_category_id',
        'task.plan_start_time',
        'task.is_assignee',
        'task.assignee',
        'task.executor',
        'task.assignee_producer',
        'task.tenant_id',
        'task.executor_tenant_id',
        'task.department_id',
        'task.json as task_json',
        "task.project_id",
        "task.executor_type",
        "task.assign_status",
        "task.created",
        "task.created_by",
        "task.updated",
        "task.manual_man_hour",
        "task.actual_man_hour",
        "task.delivery_time",
        "task.episode_id",
    ];

    public function isStandardProduction()
    {
        return true;
    }

    /**
     * 获取任务所属模块
     * @param $moduleCode
     * @return array
     */
    public function getTaskBelongModule($moduleCode = '')
    {
        $moduleIds = [];
        if (!empty($moduleCode)) {
            if ($moduleCode == 'asset') {
                $moduleIds = [
                    module_by_code('design')['id'],
                    module_by_code('asset')['id'],
                    module_by_code('level')['id'],
                ];
            } elseif ($moduleCode == 'shot') {
                $moduleIds = [
                    module_by_code('shot')['id'],
                ];
            }
        }
        return $moduleIds;
    }

    /**
     * 任务库列表查询
     * @param $data
     * @param int $tenantId
     * @param int $userId
     * @return array
     * @throws Exception
     * @throws \Exception
     */
    public function list($data, int $tenantId, int $userId)
    {
        // 默认配置
        $param = $data['param'] ?? [];
        $type = $data['type'] ?? "step";
        $page = $param['page'] ?? [1, C('default_page_total')];
        $moduleCode = $data['module_code'] ?? "";

        // 定义返回数据格式
        $res = [
            'rows' => [],
        ];

        // 指定任务id查询，不能忽略子任务
        $queryDismantleTask = false;
        if (!empty($param['filter']['task.id'])) {
            $queryDismantleTask = true;
        }

        // 限制模块查询范围大小
        $moduleIds = $this->getTaskBelongModule($moduleCode);

        // 获取任务关闭状态
        $closedStatus = StatusService::getInstance()->getTaskDefaultClosedStatus();

        $filter = $param['filter'];
        $projectId = $filter['project_id'] ?? null;

        //限制项目范围大小
        if (!$queryDismantleTask && empty($projectId)) {
            $projectList = ProjectService::getInstance()->getUserProjectList($userId, ['param' => ['project_status' => 'inprogress']]);
            $projectList = array_column($projectList['rows'], 'project');
            $projectIds = array_column($projectList, 'id');
            if (!empty($projectIds)) {
                $projectId = ["IN", $projectIds];
            }
        }

        if (!empty($projectId)) {
            $filter['task.project_id'] = $projectId;
            $filter['entity.project_id'] = $projectId;
        }

        $isFilterOverdue = $filter['is_filter_overdue'] ?? false;
        $isReceiverDeal = $filter['is_receiver_work_order'] ?? false;
        $isOwnerDeal = $filter['is_owner_work_order'] ?? false;
        $abnormal = isset($filter['task_work_order_filter']['is_abnormal']);
        $isMeExecutor = $filter['is_me_executor'] ?? false;
        $isMeReview = $filter['is_me_review'] ?? false;

        //添加序列名字查询
        if ($moduleCode == 'shot' && isset($filter['sequence_name'])) {
            $sequenceList = model(EntityModel::class)
                ->where(['project_id' => $projectId, 'name' => $filter['sequence_name'], 'module_id' => module_by_code('sequence')['id']])
                ->field('id')
                ->select();
            if (empty($sequenceList)) {
                $res['total'] = 0;
                return $res;
            }
            $sequenceIds = array_column($sequenceList, 'id');
            $filter[]['entity.entity_id'] = ['IN', $sequenceIds];
        }

        unset($filter['project_id'], $filter['is_filter_overdue'], $filter['sequence_name'], $filter['group_todo'], $filter['abnormal']);

        $filter = $this->parseMultipleEntityTaskFilter($filter);

        //需要特殊处理 的筛选
        if (!empty($moduleIds)) {
            $filter['entity.module_id'] = ['IN', join(',', $moduleIds)];
        }

        $filter['task.step_type'] = $type;
        $filter["JSON_UNQUOTE(JSON_EXTRACT(entity.json, concat('$.', module.code,'_status')))"] = ['NEQ', $closedStatus['id']];

        //是否查询逾期任务
        if ($isFilterOverdue) {
            //逾期有两种 完成逾期 ,未完成逾期
            //未完成逾期,判断是否有实际完成时间,计划完成时间小于当前时间
            //完成逾期,判定实际完成时间大于计划完成时间
            $statusList = StatusService::getInstance()->getTaskDefaultStatusWithExclude(['final', 'closed']);
            $statusIds = array_column($statusList, 'id');
            $filter[] = [
                "JSON_UNQUOTE(JSON_EXTRACT(task.json, '$.task_status'))" => ["IN", $statusIds],
                '_string' => "task.plan_end_time > 0 and task.plan_end_time <= " . time()
            ];
        }

        // 组内待办
        if (!empty($param['filter']['group_todo'])) {
            $departmentList = TaskAssignService::getInstance()->getAssignUserDepartmentList();
            if (empty($departmentList)) {
                $res['total'] = 0;
                return $res;
            }
            $projectList = ProjectService::getInstance()->getUserProjectList($userId, ['param' => ['project_status' => 'inprogress']]);
            $projectIds = array_column(array_column($projectList['rows'], 'project'), 'id');
            $departmentIds = array_column($departmentList, 'id');
            $filter['task.department_id'] = ['IN', $departmentIds];
            if (empty($filter['task.project_id'])) {
                $filter['task.project_id'] = ['IN', $projectIds];
            }
        }

        //获取未处理状态
        $untreatedWorkOrderStatus = TaskWorkOrderService::getInstance()->getOptionsMergeItemStatus("untreated");

        //是否查询自己需要处理的工单的任务
        if ($isReceiverDeal) {
            $taskIds = $this->receiverWaitDealWorkOrderTaskFilter($userId, $tenantId, $projectId, $untreatedWorkOrderStatus);
            if (empty($taskIds)) {
                $res['total'] = 0;
                return $res;
            }
            $filter[]["task.id"] = ['IN', $taskIds];
        }

        //是否查询自己提交的且未处理的工单的任务
        if ($isOwnerDeal) {
            $taskIds = $this->createdByWaitDealWorkOrderTaskFilter($userId, $tenantId, $projectId, $untreatedWorkOrderStatus);
            if (empty($taskIds)) {
                $res['total'] = 0;
                return $res;
            }
            $filter[]["task.id"] = ['IN', $taskIds];
        }

        // 仅我执行
        if ($isMeExecutor) {
            $filter[]['task.executor'] = $userId;
        }

        // 仅我审核
        if ($isMeReview) {
            if (empty($projectIds)) {
                $projectIds = [$projectId];
            }
            $versionData = VersionService::getInstance()->getMeExecutorReviewInfo($userId, $tenantId, $projectIds, $filter);
            if (empty($versionData)) {
                $res['total'] = 0;
                return $res;
            }
            $filter[]['task.id'] = ['IN', array_column($versionData, 'link_id')];
        }

        // 根据工单条件进行筛选任务
        $taskWorkOrderFilter = $param['filter']['task_work_order_filter'] ?? null;
        unset($taskWorkOrderFilter['is_abnormal']);
        if (!empty($taskWorkOrderFilter)) {
            $taskIdList = $this->queryTaskWorkOrderFilter($taskWorkOrderFilter, $userId, $tenantId, $untreatedWorkOrderStatus);
            if (empty($taskIdList)) {
                $res['total'] = 0;
                return $res;
            }
            $filter[]["task.id"] = ['IN', implode(',', $taskIdList)];
        }

        if ($abnormal) { // 是否异常
            $abnormalId = $this->queryAbnormalTaskIdList($filter);
            if (empty($abnormalId)) {
                $res['total'] = 0;
                return $res;
            }
            $filter[]['task.id'] = ['IN', $abnormalId];
        }

        $order = $param['order'] ?? 'task.order_str';
        if (!$queryDismantleTask) {
            $filter[] = [
                'step.is_dismantle_step' => 'no'
            ];
        }

        //外包要看 不属于自身项目的任务
        $isOutSource = $param['is_out_source'] ?? 0;
        if ($isOutSource) {
            unset($filter['task.project_id']);
        }

        // 先获取当前集分页范围，单个任务查询不需要处理
        $episodePageRange = ['episodes_ids' => 0];
        $useTaskFilterOffset = false;
        $taskFilterOffset = 0;
        if (!is_array($projectId) && $projectId > 0 && !$queryDismantleTask) {

            $episodePageRange = $this->getTaskPageEpisodeIds($projectId, $page, $filter);
            $res['total'] = $episodePageRange['total'] ?? 0;
            if ($res['total'] == 0) {
                return $res;
            }


            if (empty($episodePageRange['episodes_ids'])) {
                $filter['task.episode_id'] = 0;
            } else {
                $filter['task.episode_id'] = ['IN', $episodePageRange['episodes_ids']];
            }

            // 要采用limit offset来找到起点查询
            $useTaskFilterOffset = true;
            $taskFilterOffset = $episodePageRange['offset'];
        }

        $taskListQuery = ProducerManageService::getInstance()->getTaskListQuery($this->tableList, ['entity', 'module', 'step'])
            ->join('entity on entity.id = task.entity_id')
            ->join('module on entity.module_id = module.id')
            ->join('step on step.id = task.step_id')
            ->where($filter)
            ->field($this->listQueryTaskField);

        if ($useTaskFilterOffset) {
            $taskListQuery->limit($taskFilterOffset, $page[1]);
        } else {
            $taskListQuery->page($page[0], $page[1]);
        }

        if (!$queryDismantleTask) {
            // 非单个查询才需要排序
            if ($useTaskFilterOffset) {
                $taskListQuery->order('task.episode_id,' . $order);
            } else {
                $taskListQuery->order($order);
            }
        }

        $taskList = $taskListQuery->select();

        if (empty($taskList)) {
            return $res;
        }


        // 集查询
        $episodeList = [];
        $episodeIds = [];
        $executorUserId = [];
        $departmentIdMap = [];
        $tenantIds = [];
        $stepCategoryIds = [];
        $entityId = [];
        $entityParentId = [];
        $insideTaskList = [];
        $outsideTaskId = [];
        $taskStatusIds = [];
        $taskIds = [];
        $projectListIds = [];
        $tenantGroupIds = [];
        $tenantGroupTaskIds = [];
        $stepWorkFlowIdList = [];
        $stepIds = [];
        $episodeCodeList = [];
        $this->extractQueryData($type, $taskList, $executorUserId, $departmentIdMap, $tenantIds,
            $stepCategoryIds, $entityId, $entityParentId, $insideTaskList, $outsideTaskId,
            $taskStatusIds, $taskIds, $projectListIds, $tenantGroupIds, $tenantGroupTaskIds,
            $stepWorkFlowIdList, $stepIds, $episodeCodeList);


        // 获取所有子任务
        $dismantleTaskData = DismantleTaskService::getInstance()->getDismantleTaskIds($taskIds);
        $dismantleTaskParentIdMap = array_column($dismantleTaskData, 'source_task_id', 'id');
        $dismantleTaskIdListGrouped = array_group_by($dismantleTaskData, 'source_task_id');
        $dismantleTaskList = [];
        if (!$queryDismantleTask && !empty($dismantleTaskData)) {
            $dismantleTaskList = model(TaskModel::class)
                ->join('entity on entity.id = task.entity_id')
                ->join('module on entity.module_id = module.id')
                ->where(['task.id' => ['IN', array_column($dismantleTaskData, 'id')]])
                ->field($this->listQueryTaskField)
                ->select();

            $this->extractQueryData($type, $dismantleTaskList, $executorUserId, $departmentIdMap,
                $tenantIds, $stepCategoryIds, $entityId, $entityParentId, $insideTaskList,
                $outsideTaskId, $taskStatusIds, $taskIds, $projectListIds, $tenantGroupIds,
                $tenantGroupTaskIds, $stepWorkFlowIdList, $stepIds, $episodeCodeList);
        }

        $tenantGroupIds = array_values($tenantGroupIds);

        // 剧集查询
        if (!empty($episodeCodeList)) {
            $episodeIds = $episodePageRange['episodes_ids'];
        }

        //排期时间查询
        $planTimeMap = PlanService::getInstance()->getEpisodeStepCategoryPlanObject($episodeIds, $stepCategoryIds);

        // 工序流查询
        $stepNodesDict = [];
        if (!empty($stepWorkFlowIdList)) {
            $stepNodesDict = StepWorkflowService::getInstance()->getStepNodesDictByStepWorkflowId($stepWorkFlowIdList);
        }

        $projectArray = ProjectService::getInstance()->getProjects(["id" => ["IN", join(",", $projectListIds)]], "id,name,code,type");
        $projectArray = array_column($projectArray, null, "id");

        //查询实体的父级
        $entityParentList = [];
        if (!empty($entityParentId)) {
            $entityParentList = model(EntityModel::class)
                ->where(['id' => ['IN', $entityParentId]])
                ->field('id,module_id,name')
                ->cache(C('database.default_query_cache_time'))
                ->select();
            $entityParentList = array_column($entityParentList, null, 'id');
        }
        //获取外包任务映射id
        $outsideMap = ReviewTaskService::getInstance()->getOutsideMap($insideTaskList, $outsideTaskId);
        $insideTaskIdList = $outsideMap['task_ids'];
        $outsideTaskIdMap = $outsideMap['b_to_a_map'];
        $insideTaskIdMap = $outsideMap['a_to_b_map'];

        //通过外包筛选出集团任务,获取子公司的 部门 执行人 分配人
        $tenantGroupTaskIdMap = array_intersect_key($insideTaskIdMap, $tenantGroupTaskIds);
        $tenantGroupTaskList = [];
        if (!empty($tenantGroupTaskIdMap)) {
            $tenantGroupTaskIdList = array_values($tenantGroupTaskIdMap);
            $tenantGroupTaskList = model(TaskModel::class)->where(["id" => ["IN", $tenantGroupTaskIdList]])->field("id,assignee,executor,department_id")->select();
            foreach ($tenantGroupTaskList as $tenantGroupTaskItem) {
                if ($tenantGroupTaskItem['assignee'] != 0 && !in_array($tenantGroupTaskItem['assignee'], $executorUserId)) {
                    $executorUserId[] = $tenantGroupTaskItem['assignee'];
                }
                if ($tenantGroupTaskItem['executor'] != 0 && !in_array($tenantGroupTaskItem['executor'], $executorUserId)) {
                    $executorUserId[] = $tenantGroupTaskItem['executor'];
                }
                if ($tenantGroupTaskItem['department_id'] != 0 && !in_array($tenantGroupTaskItem['department_id'], $departmentIdMap)) {
                    $departmentIdMap[] = $tenantGroupTaskItem['department_id'];
                }
            }
            $tenantGroupTaskList = array_column($tenantGroupTaskList, null, 'id');
        }

        $fields = [
            "review_request.id",
            "review_request.name",
            "review_request.created",
            "review_request_node.id",
            "review_request_node.is_pass",
            "review_request_node.is_reviewed",
            "review_request_node.reviewed",
            "review_request_node.start_at",
            "review_request_node.plan_reviewer",
            "review_request.link_id",
        ];
        // 审核版本map
        $reviewDataMap = [];
        //版本查询
        $maxVersion = VersionService::getInstance()->getTaskMaxVersionInfo($insideTaskIdList, "id,number,link_id,param,is_publish,is_pass,description");

        // 审核进度
        $reviewProgressData = [];
        $versionReviewRequestMap = [];
        if (!empty($maxVersion)) {
            $versionModuleData = module_by_code('version');
            $reviewData = model(ReviewRequestModel::class)
                ->join("review_request_node on review_request.id = review_request_node.review_request_id")
                ->where(['review_request.link_id' => ['IN', array_column($maxVersion, 'id')]])
                ->where(['review_request.module_id' => $versionModuleData['id']])
                ->where(['review_request_node.is_active' => 'yes', 'review_request.tenant_id' => $tenantId])
                ->field(build_complex_query_fields($fields, '__'))
                ->select();
            $reviewRequestList = model(ReviewRequestModel::class)
                ->where(['module_id' => $versionModuleData['id'], 'link_id' => ['IN', array_column($maxVersion, 'id')]])
                ->select();
            $versionReviewRequestMap = array_column($reviewRequestList, 'id', 'link_id');
            $reviewRequestIdList = array_column($reviewRequestList, 'id');
            foreach ($reviewData as &$item) {
                $item = separate_complex_query_data($item, '__');
                $item['link_id'] = $item['review_request']['link_id'];
            }
            $reviewDataMap = array_column($reviewData, null, 'link_id');
            $reviewProgressData = ReviewRequestService::getInstance()->queryReviewRequestProgressData($reviewRequestIdList);
        }

        $version = [];
        foreach ($maxVersion as $maxVersionItem) {
            if (isset($outsideTaskIdMap[$maxVersionItem['link_id']])) {
                $version[$outsideTaskIdMap[$maxVersionItem['link_id']]] = $maxVersionItem;
            } else {
                $version[$maxVersionItem['link_id']] = $maxVersionItem;
            }
        }

        $userInfo = TaskService::getInstance()->queryTaskSupplierInfo($tenantId, [], [], $departmentIdMap, $executorUserId, array_values($tenantIds), $tenantGroupIds);
        if (!empty($userInfo["tenant_group"])) {
            $userInfo["tenant_group"] = array_column($userInfo["tenant_group"], null, 'id');
        }

        $statusList = StatusService::getInstance()->getList([], 'id,name,code,color,icon');
        $statusList = array_column($statusList, null, 'id');

        // 填充 step
        $stepDict = [];
        if (!empty($stepIds)) {
            $stepData = StepService::getInstance()->getList(['id' => ['IN', join(',', $stepIds)]], 'id,name,code,color,can_multiple_submit,need_assemble,is_dismantle_step');
            $stepDict = array_column($stepData, null, 'id');
        }

        $dismantleTaskListMap = [];

        // 查询任务属性字符串
        //task attr
        $taskAttr = InsideTaskReviewWorkflowService::getInstance()->getMultipleTaskAttr($taskIds);
        $taskWorkOrderListStatusCheckList = model(TaskWorkOrderModel::class)
            ->where(['task_id' => ['IN', $taskIds], 'status' => ['IN', TaskWorkOrderModel::STATUS_NOT_CLOSED_GROUP]])
            ->field("task_id,concat(task_id,'#',status) ck")
            ->group('task_id,status')
            ->select();
        $notClosedWorkOrderTaskIdList = array_column($taskWorkOrderListStatusCheckList, 'task_id', 'task_id');
        $taskWorkOrderListStatusCheckList = array_column($taskWorkOrderListStatusCheckList, 'ck', 'ck');

        // 未关闭工单处理
        $doneStatus = StatusService::getInstance()->getTaskDoneStatus();
        $notStartedStatus = StatusService::getInstance()->getNotStartedStatus();
        $notStartedStatus = array_column($notStartedStatus, 'id');
        $currentTime = time();

        //补充 任务状态,版本,执行人,分配人,部门,租户 信息
        $taskDealFunction = function ($taskItem) use ($statusList, $entityParentList, $projectArray, $dismantleTaskIdListGrouped, $taskAttr, $stepNodesDict, &$dismantleTaskListMap, $taskWorkOrderListStatusCheckList, $closedStatus, $doneStatus, $notStartedStatus, $currentTime, $planTimeMap, $notClosedWorkOrderTaskIdList) {
            $taskItem['status'] = $statusList[$taskItem['task_status']] ?? null;
            $taskItem['entity_status'] = $statusList[$taskItem['entity_status_id']] ?? null;
            $taskItem['task_attr'] = $taskAttr[$taskItem['id']] ?? '';
            //填充父级实体
            if ($taskItem['entity_parent_id'] > 0 && isset($entityParentList[$taskItem['entity_parent_id']])) {
                $entityParentModuleCode = module_by_id($entityParentList[$taskItem['entity_parent_id']]['module_id'])['code'];
                unset($entityParentList[$taskItem['entity_parent_id']]['module_id']);
                $taskItem[$entityParentModuleCode] = $entityParentList[$taskItem['entity_parent_id']];
            }

            if (isset($projectArray[$taskItem['project_id']])) {
                $taskItem['project'] = $projectArray[$taskItem['project_id']];
            }
//            $taskItem['episode_id'] = $episodeList[$taskItem['initial_episode_code']]['id'] ?? '';
            $taskItem['dismantle_task_ids'] = $dismantleTaskIdListGrouped[$taskItem['id']] ?? [];
            $taskItem['children'] = [];
            foreach ($taskItem['dismantle_task_ids'] as $dismantleTaskItem) {
                if (isset($dismantleTaskListMap[$dismantleTaskItem['id']])) {
                    $taskItem['children'][] = $dismantleTaskListMap[$dismantleTaskItem['id']];
                }
            }
            //存在属性排期
            $categoryKey = $taskItem['module_code'].'_category';

            $taskItem['module_code'] = in_array($taskItem['module_code'], ['design', 'asset', 'level', '']) ? 'asset' : 'shot';
            $taskItem['is_dismantle_task'] = (bool)$taskItem['is_dismantle_task'];

            if ((empty($taskItem['parent_id']) || $taskItem['parent_id'] == 0) && ($taskItem['is_dismantle_task'] && $taskItem['parent_task_id'] > 0)) {
                $taskItem['parent_id'] = $taskItem['parent_task_id'];
            }
            $taskItem['in_step_workflow'] = true;
            if ($taskItem['step_workflow_id'] > 0 && !empty($stepNodesDict[$taskItem['step_workflow_id']]) && !in_array($taskItem['step_id'], $stepNodesDict[$taskItem['step_workflow_id']]['step_id_list'])) {
                $taskItem['in_step_workflow'] = false;
            }

            // 计划交付时间
            $taskItem['delivery_plan'] = $planTimeMap[$taskItem['episode_id']][$taskItem['step_category_id']] ?? null;
            //存在属性排期
            if (!empty($taskItem['entity_attr'][$categoryKey]) && !empty($planTimeMap[$taskItem['episode_id']][$taskItem['step_category_id'].'#'.$taskItem['entity_attr'][$categoryKey]])) {
                $taskItem['delivery_plan'] = $planTimeMap[$taskItem['episode_id']][$taskItem['step_category_id'].'#'.$taskItem['entity_attr'][$categoryKey]] ?? null;
            }

            $taskItem['abnormal_detail'] = [
                'overdue' => 0,
                'not_started' => false,
                'not_assigned' => false,
                'work_order_not_handled' => false
            ];

            if ($taskItem['step_type'] == TaskModel::STEP_TYPE_STEP) {
                $taskItem['has_unclose_work_order'] = $notClosedWorkOrderTaskIdList[$taskItem['id']] ?? 0;
                if ($taskItem['plan_start_time'] > 0 && $taskItem['plan_start_time'] < $currentTime && $taskItem['assign_status'] == TaskModel::ASSIGN_STATUS_DEFAULT) {
                    $taskItem['abnormal_detail']['not_assigned'] = true;
                }
                if ($taskItem['plan_start_time'] > 0 && $taskItem['plan_start_time'] < $currentTime && in_array($taskItem['task_status'], $notStartedStatus)) {
                    $taskItem['abnormal_detail']['not_started'] = true;
                }
                if (isset($taskWorkOrderListStatusCheckList[$taskItem['id'] . '#' . TaskWorkOrderModel::STATUS_UNTREATED])) {
                    $taskItem['abnormal_detail']['work_order_not_handled'] = true;
                }
            }
            if (!in_array($taskItem['task_status'], [$closedStatus['id'], $doneStatus['id']]) && $taskItem['plan_end_time'] < $currentTime && $taskItem['plan_end_time'] > 0) {
                $taskItem['abnormal_detail']['overdue'] = time() - $taskItem['plan_end_time'];
            }

            unset($taskItem['assignee'], $taskItem['entity_json'], $taskItem['task_json'], $taskItem['executor'], $taskItem['executor_tenant_id'], $taskItem['tenant_id'], $taskItem['department_id'], $taskItem['entity_parent_id']);
            return $taskItem;
        };

        $taskDealStepCategoryFunction = $taskDealFunction;

        //补充工序分类名称
        $stepCategory = [];
        if (!empty($stepCategoryIds)) {
            $stepCategory = StepCategoryService::getInstance()->getList(['id' => ['IN', $stepCategoryIds]], 'id,name,code');
            $stepCategory = array_column($stepCategory, null, 'id');
        }

        $projectId = $param['filter']['project_id'] ?? 0;
        $taskDelayMap = ReviewTaskDelayService::getInstance()->taskDelayInfo($taskIds, $projectId);

        $taskDealFunction = function ($taskItem) use (
            &$taskDealStepCategoryFunction, $stepCategory, $userInfo, $version, $untreatedWorkOrderStatus,
            $tenantGroupTaskIdMap, $tenantGroupTaskList, $reviewDataMap, $tenantId, $stepDict, $reviewProgressData, $versionReviewRequestMap,
            $taskDelayMap
        ) {
            $taskItem['step_category_name'] = $stepCategory[$taskItem['step_category_id']]['name'];
            $taskItem['step_category_code'] = $stepCategory[$taskItem['step_category_id']]['code'];
            // 公司 部门 执行人 分配人 分配制片
            $currentTenant = $taskItem['tenant_id'];
            $taskItem['assignee_info'] = $taskItem['executor_info'] = $taskItem['department_info'] = $taskItem['created_by_info'] = null;
            if ($taskItem['executor_tenant_id'] > 0 && $taskItem['executor_tenant_id'] != $taskItem['tenant_id']) {
                $currentTenant = $taskItem['executor_tenant_id'];
                if (!empty($userInfo["tenant_group"][$taskItem["tenant_group_id"]])) {
                    $taskItem["tenant_group"] = $userInfo["tenant_group"][$taskItem["tenant_group_id"]];
                }
                if ($taskItem["executor_type"] == "tenant_group" && isset($tenantGroupTaskIdMap[$taskItem["id"]]) && isset($tenantGroupTaskList[$tenantGroupTaskIdMap[$taskItem["id"]]])) {
                    $currentTaskItem = $tenantGroupTaskList[$tenantGroupTaskIdMap[$taskItem["id"]]];
                    $taskItem['assignee_info'] = $userInfo['executor'][$currentTaskItem['assignee']] ?? null;
                    $taskItem['executor_info'] = $userInfo['executor'][$currentTaskItem['executor']] ?? null;
                    $taskItem['department_info'] = $userInfo['department'][$currentTaskItem['department_id']] ?? null;
                }
            } else {
                $taskItem['assignee_info'] = $userInfo['executor'][$taskItem['assignee']] ?? null;
                $taskItem['executor_info'] = $userInfo['executor'][$taskItem['executor']] ?? null;
                $taskItem['department_info'] = $userInfo['department'][$taskItem['department_id']] ?? null;
            }

            $taskItem['created_by_info'] = $userInfo['executor'][$taskItem['created_by']] ?? null;
            $taskItem["assignee_producer"] = $userInfo['executor'][$taskItem['assignee_producer']] ?? null;
            $taskItem['tenant_info'] = $userInfo['tenant'][$currentTenant] ?? null;
            $taskItem['version'] = $version[$taskItem['id']] ?? null;
            $linkId = $taskItem['version']['id'] ?? '';
            $reviewRequestId = $versionReviewRequestMap[$linkId] ?? 0;
            // 审核信息
            $taskItem['review_request'] = $reviewDataMap[$linkId]['review_request'] ?? null;
            // 审核节点信息
            $taskItem['review_request_node'] = $reviewDataMap[$linkId]['review_request_node'] ?? null;
            // 审核进度
            $taskItem['review_progress'] = null;
            if ($reviewRequestId > 0) {
                $taskItem['review_progress'] = $reviewProgressData[$reviewRequestId] ?? null;
            }
            // 是否外包
            $taskItem['is_outside'] = false;
            if ($taskItem['executor_tenant_id'] > 0 && $taskItem['executor_tenant_id'] != $tenantId) {
                $taskItem['is_outside'] = true;
            }
            // 工序
            $taskItem['step'] = $stepDict[$taskItem['step_id']] ?? null;
            // 工作量剩余
            $taskItem['task_workload_remain'] = !is_null($taskItem['task_workload_remain']) ? intval($taskItem['task_workload_remain']) : 100;
            // 是否申请延期中
            $taskItem['is_task_delay'] = !empty($taskDelayMap[$taskItem['id']]);
            // 延期时间
            $taskItem['task_delay_time'] = $taskDelayMap[$taskItem['id']]['task_delay_time'] ?? 0;
            // 延期原因
            $taskItem['task_delay_reason'] = $taskDelayMap[$taskItem['id']]['task_delay_reason'] ?? null;
            return $taskDealStepCategoryFunction($taskItem);
        };
        $res['rows'] = array_map($taskDealFunction, $taskList);
        // 先格式化子任务
        $dismantleTaskList = array_map($taskDealFunction, $dismantleTaskList);

        // 构建子任务map
        foreach ($dismantleTaskList as &$dismantleTaskItem) {
            $dismantleTaskItem['parent_id'] = $dismantleTaskParentIdMap[$dismantleTaskItem['id']] ?? 0;
            $res['rows'][] = &$dismantleTaskItem;
        }
        return $res;
    }

    /**
     * 工单处理状态展示顺序
     * @param $oldStatus
     * @param $newStatus
     * @return mixed
     */
    public function taskWorkOrderStatusOrder($oldStatus, $newStatus)
    {
        // 顺序 0-未处理 1-以处理 2-以失效
        return min($oldStatus, $newStatus);
    }

    /**
     * 获取审核信息
     * @param $tenantId
     * @param $versionId
     * @return array|array[]
     * @throws \Exception
     */
    public function getReviewInfo($tenantId, $versionId)
    {
        //当前公司审核人员的进度
        $playlistItemVersion = model(PlaylistItemVersionModel::class)->where(['tenant_id' => $tenantId, 'version_id' => ['IN', $versionId]])
            ->field('version_id,review_request_id,current_review_code')->select();


        if (empty($playlistItemVersion)) {
            return ['version_review_request_map' => [], 'review_request_node_group' => []];
        }

        $reviewRequestIds = [];
        $versionReviewRequestMap = [];
        $reviewRequestCurrentCodeMap = [];
        foreach ($playlistItemVersion as $playlistItemVersionItem) {
            $reviewRequestIds[] = $playlistItemVersionItem['review_request_id'];
            $reviewRequestCurrentCodeMap[$playlistItemVersionItem['review_request_id']] = $playlistItemVersionItem['current_review_code'];
            $versionReviewRequestMap[$playlistItemVersionItem['version_id']] = $playlistItemVersionItem['review_request_id'];
        }

        $reviewRequestNodeList = model(ReviewRequestNodeModel::class)
            ->where(['review_request_id' => ['IN', $reviewRequestIds]])->field('parent_id,role_code,review_request_id,plan_reviewer')->select();

        if (empty($reviewRequestNodeList)) {
            return ['version_review_request_map' => [], 'review_request_node_group' => []];
        }

        $reviewRequestNodeGroup = ReviewTaskService::getInstance()->dealReviewSchedule($reviewRequestNodeList, $reviewRequestCurrentCodeMap);

        return ['version_review_request_map' => $versionReviewRequestMap, 'review_request_node_group' => $reviewRequestNodeGroup];
    }

    /**
     * 获取实体工序分类的最后一个工序任务的映射
     */
    public function getStepCategoryMaxStepTaskMap($entityId)
    {
        $stepMaxTask = model(TaskModel::class)
            ->where(['task.entity_id' => ['IN', $entityId], 'task.step_type' => 'step'])
            ->field('max(id) as id')->group('entity_id,step_category_id')->select();

        $stepTaskMap = [];
        $taskStatusIds = [];
        if (!empty($stepMaxTask)) {
            $stepMaxTaskIds = array_column($stepMaxTask, 'id');
            $stepTask = model(TaskModel::class)
                ->where(['task.id' => ['IN', $stepMaxTaskIds]])
                ->field("task.entity_id,task.step_category_id,task.plan_end_time,task.plan_start_time,task.end_time,task.assignee,
                JSON_UNQUOTE(JSON_EXTRACT(json,'$.task_status')) as task_status")->select();

            foreach ($stepTask as $stepTaskItem) {
                if ($stepTaskItem['task_status'] > 0 && !in_array($stepTaskItem['task_status'], $taskStatusIds)) {
                    $taskStatusIds[] = $stepTaskItem['task_status'];
                }

                if (empty($stepTaskMap[$stepTaskItem['entity_id']])) {
                    $stepTaskMap[$stepTaskItem['entity_id']] = [$stepTaskItem['step_category_id'] => $stepTaskItem];
                } else {
                    $stepTaskMap[$stepTaskItem['entity_id']][$stepTaskItem['step_category_id']] = $stepTaskItem;
                }
            }
        }

        return [$stepTaskMap, $taskStatusIds];
    }


    /**
     * 接收者待处理的工单
     * @param $userId
     * @param $tenantId
     * @param $projectId
     * @param $status
     * @return array
     */
    public function receiverWaitDealWorkOrderTaskFilter($userId, $tenantId, $projectId, $status)
    {
        $taskWorkOrderFilter = ["receiver" => $userId, "tenant_id" => $tenantId, "status" => ["IN", $status]];
        if (!empty($projectId)) {
            $taskWorkOrderFilter["project_id"] = $projectId;
        }

        $taskWorkOrder = model(TaskWorkOrderModel::class)
            ->where($taskWorkOrderFilter)
            ->field("task_id")->select();

        return array_column($taskWorkOrder, 'task_id');
    }

    /**
     * 创建者待处理的工单
     * @param $userId
     * @param $tenantId
     * @param $projectId
     * @param $status
     * @return array
     */
    public function createdByWaitDealWorkOrderTaskFilter($userId, $tenantId, $projectId, $status)
    {
        $taskWorkOrderFilter = ["created_by" => $userId, "tenant_id" => $tenantId, "status" => ["IN", $status]];
        if (!empty($projectId)) {
            $taskWorkOrderFilter["project_id"] = $projectId;
        }

        $taskWorkOrder = model(TaskWorkOrderModel::class)
            ->where($taskWorkOrderFilter)
            ->field("task_id")->select();

        return array_column($taskWorkOrder, 'task_id');
    }

    /**
     * 获取会话的工单未读
     * @param $userId
     * @param $tenantId
     * @param array $taskWorkOrderIds
     * @return array
     */
    public function getTaskWorkChatIsUnread($userId, $tenantId, $taskWorkOrderIds = [])
    {
        $unionId = model(UserModel::class)->where(['id' => $userId])->value("union_id");
        $param = [
            "filter" => [
                'link_type' => 'task_work_order',
                'user_id' => $unionId,
                'from_tenant_id' => $tenantId,
            ]
        ];
        if (!empty($taskWorkOrderIds)) {
            $param['filter']['link_id'] = ["-in", join(",", $taskWorkOrderIds)];
        }
        $requestData = [
            "param" => $param
        ];
        $chat = teamones_request('teamones-im', 'chat/get_chat_is_unread', $requestData);
        $chat = $chat['data'];
        return array_column($chat, 'link_id');
    }

    /**
     * 查询异常任务idList
     * @param array $baseFilter
     * @return array
     */
    public function queryAbnormalTaskIdList(array $baseFilter)
    {
        $abnormalFilter = $baseFilter;
        $notStartedStatusList = StatusService::getInstance()->getNotStartedStatus();
        $doneStatus = StatusService::getInstance()->getTaskDoneStatus();
        $closedStatus = StatusService::getInstance()->getTaskDefaultClosedStatus();
        $abnormalFilter[] = [
            [
                [
                    'task.step_type' => TaskModel::STEP_TYPE_STEP,
                    'task.end_time' => 0,
                    [
                        'task.assign_status' => 'default',
                        "task.json->>'$.task_status'" => ['IN', implode(',', array_column($notStartedStatusList, 'id'))],
                        '_logic' => 'OR',
                    ],
                    'task.plan_end_time' => ['BETWEEN', [1, time()]],
                    "task.json->>'$.task_status'" => ['NOT IN', [$doneStatus['id'], $closedStatus['id']]],
                ],
                [
                    'task.end_time' => 0,
                    'task.plan_end_time' => ['BETWEEN', [1, time()]],
                    "task.json->>'$.task_status'" => ['NOT IN', [$doneStatus['id'], $closedStatus['id']]],
                ],
                ['task_work_order_temp.task_id' => ['EXP', 'IS NOT NULL'],],
                '_logic' => 'or',
            ],
            [
                "task.json->>'$.task_status'" => ['NEQ', $closedStatus['id']]
            ],
        ];

        $taskIdList = model(TaskModel::class)
            ->join('entity on task.entity_id = entity.id', 'LEFT')
            ->join('module on module.id = entity.module_id', 'LEFT')
            ->join('(select task_id from task_work_order where status = ' . TaskWorkOrderModel::STATUS_UNTREATED . ' group by task_id) task_work_order_temp on task_work_order_temp.task_id = task.id', 'LEFT')
            ->where($abnormalFilter)
            ->field('task.id')
            ->group('task.id')
            ->select();
        return array_column($taskIdList, 'id');
    }

    /**
     * 工单查询筛选任务IdList
     * @param $taskWorkOrderFilter
     * @param int $userId
     * @param int $tenantId
     * @param $untreatedWorkOrderStatus
     * @return array
     */
    private function queryTaskWorkOrderFilter($taskWorkOrderFilter, int $userId, int $tenantId, $untreatedWorkOrderStatus)
    {
        $taskFilter = [
            'task.step_type' => 'step'
        ];
        // 筛选工单状态
        if (isset($taskWorkOrderFilter['task_work_order_status'])) {
            $taskFilter[] = ['task_work_order.status' => $taskWorkOrderFilter['task_work_order_status']];
        }
        // 筛选工单有消息
        if (isset($taskWorkOrderFilter['task_work_order_message'])) {
            $taskWorkOrderUnreadIds = $this->getTaskWorkChatIsUnread($userId, $tenantId);
            if (empty($taskWorkOrderUnreadIds)) {
                return [];
            }
            $taskFilter[] = ['task_work_order.id' => ['IN', implode(',', $taskWorkOrderUnreadIds)]];
        }
        // 筛选任务异常状态，如果没有工单或者指定类型工单全部处理就是正常
//        if (isset($taskWorkOrderFilter['is_abnormal'])) {
//            $taskFilter[] = ['task_work_order.type' => ['IN', $this->taskWorkOrderType], 'task_work_order.status' => ["IN", $untreatedWorkOrderStatus]];
//        }
        $taskData = model(TaskModel::class)
            ->join('task_work_order on task_work_order.task_id = task.id', 'left')
            ->join('task_work_order_user on task_work_order_user.work_order_id = task_work_order.id', 'left')
            ->field('task.id')
            ->where($taskFilter)
            ->group('task.id')
            ->select();
        return array_column($taskData, 'id');
    }

    /**
     * 拆分数据
     * @param $type
     * @param $taskList
     * @param array $executorUserId
     * @param array $departmentIdMap
     * @param array $tenantIds
     * @param array $stepCategoryIds
     * @param array $entityId
     * @param array $entityParentId
     * @param array $insideTaskList
     * @param array $outsideTaskId
     * @param array $taskStatusIds
     * @param array $taskIds
     * @param array $projectListIds
     * @param array $tenantGroupIds
     * @param array $tenantGroupTaskIds
     * @param array $stepWorkFlowIdList
     * @param array $stepIds
     * @param array $episodeCodeList
     * @return void
     */
    public function extractQueryData($type, &$taskList, array &$executorUserId, array &$departmentIdMap,
                                     array &$tenantIds, array &$stepCategoryIds, array &$entityId,
                                     array &$entityParentId, array &$insideTaskList, array &$outsideTaskId,
                                     array &$taskStatusIds, array &$taskIds, array &$projectListIds,
                                     array &$tenantGroupIds, array &$tenantGroupTaskIds, array &$stepWorkFlowIdList,
                                     array &$stepIds, array &$episodeCodeList)
    {
        foreach ($taskList as &$taskItem) {
            $taskJson = json_decode($taskItem['task_json'], true);
            $taskItem['task_status'] = $taskJson['task_status'] ?? null;
            $taskItem['tenant_group_id'] = $taskJson['tenant_group_id'] ?? null;
            $taskItem['is_dismantle_task'] = $taskJson['is_dismantle_task'] ?? null;
            $taskItem['parent_task_id'] = $taskJson['parent_task_id'] ?? null;
            $taskItem['dismantle_name'] = $taskJson['dismantle_name'] ?? null;
            $taskItem['shot_duration'] = $taskJson['shot_duration'] ?? null;
            $taskItem['task_workload_remain'] = $taskJson['task_workload_remain'] ?? null;
            $taskItem['is_lock'] = $taskJson['is_lock'] ?? "no";
            $taskItem['team'] = $taskJson['team'] ?? [];

            $entityJson = json_decode($taskItem['entity_json'], true);

            $upstreamEntityId = 0;
            if (!in_array($taskItem['module_code'], ['shot', 'level', 'asset'])) {
                $upstreamEntityId = $taskItem['entity_id'];
            } else {
                $isShot = $taskItem['module_code'] == 'shot';
                $upstreamEntityField = $taskItem['module_code'] . ($isShot ? '_storyboard' : '_design');
                $upstreamEntityId = $entityJson[$upstreamEntityField] ?? 0;
            }
            $taskItem['upstream_entity_id'] = $upstreamEntityId;

            $currentEntityModuleCode = module_by_id($taskItem['entity_module_id'])['code'];
            $taskItem['grade'] = $entityJson[$currentEntityModuleCode . '_grade'] ?? '';
            $taskItem['category'] = $entityJson[$currentEntityModuleCode . '_category'] ?? '';
            $taskItem['entity_status_id'] = $entityJson[$currentEntityModuleCode . '_status'] ?? '';

            request()->getModuleCode($currentEntityModuleCode);
            $taskItem['entity_attr'] = model(EntityModel::class)->getJsonAttr($taskItem['entity_json'], []);
            if (!empty($taskItem['initial_episode_code'])) {
                $episodeCodeList[$taskItem['initial_episode_code']] = $taskItem['initial_episode_code'];
            }
            if ($taskItem['entity_id'] != 0 && !in_array($taskItem['entity_id'], $entityId)) {
                $entityId[] = $taskItem['entity_id'];
            }
            if ($taskItem['step_id'] != 0 && !in_array($taskItem['step_id'], $stepIds)) {
                $stepIds[] = $taskItem['step_id'];
            }
            if ($taskItem['entity_parent_id'] != 0 && !in_array($taskItem['entity_parent_id'], $entityParentId)) {
                $entityParentId[] = $taskItem['entity_parent_id'];
            }
            if ($taskItem['step_workflow_id'] > 0 && !in_array($taskItem['step_workflow_id'], $stepWorkFlowIdList)) {
                $stepWorkFlowIdList[] = $taskItem['step_workflow_id'];
            }

            $projectListIds[$taskItem['project_id']] = $taskItem['project_id'];

            //只有工序任务查询 公司 部门 执行人 分配人
            if ($type === 'step') {
                if (!in_array($taskItem['step_category_id'], $stepCategoryIds)) {
                    $stepCategoryIds[] = $taskItem['step_category_id'];
                }

                //其他公司不显示 执行人,部门 , 外包任务id
                //集团化的也需要获取 部门 执行人 分配人 (但是只能通过子公司的任务获取)
                if ($taskItem['executor_tenant_id'] > 0 && $taskItem['executor_tenant_id'] != $taskItem['tenant_id']) {
                    $outsideTaskId[] = $taskItem['id'];
                    $tenantIds[$taskItem['executor_tenant_id']] = $taskItem['executor_tenant_id'];
                    $tenantGroupIds[$taskItem["tenant_group_id"]] = $taskItem["tenant_group_id"];

                    if ($taskItem["executor_type"] == "tenant_group") {
                        $tenantGroupTaskIds[$taskItem["id"]] = $taskItem["id"];
                    }
                } else {
                    $insideTaskList[] = $taskItem['id'];
                    $tenantIds[$taskItem['tenant_id']] = $taskItem['tenant_id'];
                    if ($taskItem['assignee'] != 0 && !in_array($taskItem['assignee'], $executorUserId)) {
                        $executorUserId[] = $taskItem['assignee'];
                    }
                    if ($taskItem['executor'] != 0 && !in_array($taskItem['executor'], $executorUserId)) {
                        $executorUserId[] = $taskItem['executor'];
                    }
                    if (!empty($taskItem['assignee_producer']) && $taskItem['assignee_producer'] != 0 && !in_array($taskItem['assignee_producer'], $executorUserId)) {
                        $executorUserId[] = $taskItem['assignee_producer'];
                    }
                    if ($taskItem['department_id'] != 0 && !in_array($taskItem['department_id'], $departmentIdMap)) {
                        $departmentIdMap[] = $taskItem['department_id'];
                    }
                }

                if (!in_array($taskItem['created_by'], $executorUserId)) {
                    $executorUserId[] = $taskItem['created_by'];
                }
                if ($taskItem['task_status'] > 0 && !in_array($taskItem['task_status'], $taskStatusIds)) {
                    $taskStatusIds[] = $taskItem['task_status'];
                }
                if ($taskItem['entity_status_id'] > 0 && !in_array($taskItem['entity_status_id'], $taskStatusIds)) {
                    $taskStatusIds[] = $taskItem['entity_status_id'];
                }
            }
            $taskIds[] = $taskItem['id'];
        }

    }

    /**
     * 找到指定项目任务查询，指定分页所属集数范围，缩小任务排序范围
     * @param $projectId
     * @param $page
     * @param $taskFilter
     * @return array
     */
    public function getTaskPageEpisodeIds($projectId, $page = [1, 1], $taskFilter = [])
    {
        // 获取集数module id module_by_code(['episode'])['id'];
        // 1.先获取指定项目集数的 id 和 code，按code排序
        $episodesModuleId = module_by_code('episode')['id'];

        // 1.1 取出集数数据

        // 复杂任务条件查询
        $isComplexTaskQuery = false;
        if (empty($taskFilter)) {
            $filter = [
                "entity.project_id" => $projectId,
                "entity.module_id" => $episodesModuleId
            ];
        } else {
            $isComplexTaskQuery = true;
            $filter = $taskFilter;
        }

        $cacheFilterMd5 = md5(json_encode($filter));
        $cacheIsSealEpisodeKey = "task_page_episode_ids_seal" . $cacheFilterMd5;
        $cacheIsNotSealEpisodeKey = "task_page_episode_ids_not_seal" . $cacheFilterMd5;

        $cacheIsSealEpisodeDataJson = Redis::get($cacheIsSealEpisodeKey);
        $cacheIsNotSealEpisodeDataJson = Redis::get($cacheIsNotSealEpisodeKey);

        $existIsSealEpisodeData = false;
        if (!empty($cacheIsSealEpisodeDataJson)) {
            $existIsSealEpisodeData = true;
        }

        $existIsNotSealEpisodeData = false;
        if (!empty($cacheIsNotSealEpisodeDataJson)) {
            $existIsNotSealEpisodeData = true;
        }

        // 判断当前处理问题情况类型
        $cacheRangeType = '';
        $cacheIsSealEpisodeData = [];
        $cacheIsNotSealEpisodeDataDict = [];
        $cacheIsNotSealEpisodeData = [];
        if ($existIsSealEpisodeData && $existIsNotSealEpisodeData) {
            // 两种类型缓存都存在
            $cacheRangeType = 'All';
            $cacheIsSealEpisodeData = json_decode($cacheIsSealEpisodeDataJson, true);
            $cacheIsNotSealEpisodeDataDict = json_decode($cacheIsNotSealEpisodeDataJson, true);
            $cacheIsNotSealEpisodeData = $cacheIsNotSealEpisodeDataDict['episode_count'];
        }
        if (!$existIsSealEpisodeData && $existIsNotSealEpisodeData) {
            // 仅存在未封板集数的缓存
            $cacheRangeType = 'isNotSeal';
            $cacheIsNotSealEpisodeDataDict = json_decode($cacheIsNotSealEpisodeDataJson, true);
            $cacheIsNotSealEpisodeData = $cacheIsNotSealEpisodeDataDict['episode_count'];
        }
        if ($existIsSealEpisodeData && !$existIsNotSealEpisodeData) {
            // 仅存在封板集数的缓存
            $cacheRangeType = 'isSeal';
            $cacheIsSealEpisodeData = json_decode($cacheIsSealEpisodeDataJson, true);
        }
        if (!$existIsSealEpisodeData && !$existIsNotSealEpisodeData) {
            // 两种类型缓存都不存在
            $cacheRangeType = 'None';
        }

        if ($cacheRangeType === 'All') {
            // 缓存都存在
            $taskGroupCountByEpisode = array_merge($cacheIsSealEpisodeData, $cacheIsNotSealEpisodeData);
            $episodesDataSortByCode = $cacheIsNotSealEpisodeDataDict['episode_sort'];
        } else {
            // 获取当前项目所有集数数据，通过PHP来按照code和封板状态来排序
            $episodesDataList = model(EntityModel::class)
                ->field('id,code,' . "JSON_UNQUOTE(JSON_EXTRACT(entity.json, '$.episode_is_seal')) as episode_is_seal")
                ->where([
                    'project_id' => $projectId,
                    "module_id" => $episodesModuleId
                ])
                ->select();

            $episodesDataByCode = [];
            $episodesDataByIsSeal = [];

            // 已经封板的集数
            $episodesIsSealList = [];
            // 未封板的集数
            $episodesIsNotSealList = [];
            // 全量集数id
            $episodesIds = [];

            foreach ($episodesDataList as $episodesDataItem) {
                $episodesDataByCode[] = $episodesDataItem['code'];
                $episodesDataByIsSeal[] = $episodesDataItem['episode_is_seal'];

                $episodesIds[] = $episodesDataItem['id'];
                if ($episodesDataItem['episode_is_seal'] === 'yes') {
                    $episodesIsSealList[] = $episodesDataItem['id'];
                } else {
                    $episodesIsNotSealList[] = $episodesDataItem['id'];
                }
            }

            // 执行排序
            array_multisort($episodesDataByIsSeal, SORT_DESC, $episodesDataByCode, SORT_ASC, $episodesDataList);

            // 1.2 整理得到集数数据，没有集数数据直接返回空
            if (empty($episodesDataList)) {
                return [];
            }

            // 组装过滤条件
            if (!$isComplexTaskQuery) {
                $filter = ['task.episode_id' => ['IN', $episodesIds]];
            }

            // 判断缓存类型缩小数据查询范围
            switch ($cacheRangeType) {
                case 'isNotSeal':
                    $filter[] = ['task.episode_id' => ['IN', $episodesIsSealList]];
                    break;
                case 'isSeal':
                    $filter[] = ['task.episode_id' => ['IN', $episodesIsNotSealList]];
                    break;
            }

            $episodesDataSortByCode = [];
            if ($isComplexTaskQuery) {
                $taskGroupCountByEpisode = model(TaskModel::class)
                    ->join('entity on entity.id = task.entity_id')
                    ->join('entity as entity_episode on  entity_episode.id = task.episode_id AND entity_episode.module_id = ' . $episodesModuleId)
                    ->join('module on entity.module_id = module.id')
                    ->join('step on step.id = task.step_id')
                    ->where($filter)
                    ->group('task.episode_id')
                    ->order('entity_episode.code')
                    ->field('DISTINCT(task.episode_id) AS id,entity_episode.code,count( task.id ) AS total ')
                    ->select();
            } else {
                // 获取集数ids列表
                $episodesDataSortByCode = $episodesDataList;
                $taskGroupCountByEpisode = model(TaskModel::class)
                    ->field('episode_id as id,count(id) AS total')
                    ->where($filter)
                    ->group('episode_id')
                    ->select();
            }

            // 处理数据缓存，把已经封板的集数和未封板集数缓存时间拆开
            // 已经封板的集数缓存 600s = 10分钟
            // 未封板的集数缓存 10s
            switch ($cacheRangeType) {
                case 'isNotSeal':
                    // 未封板的集数数据存在
                    $cacheIsSealEpisodeData = $taskGroupCountByEpisode;
                    $taskGroupCountByEpisode = array_merge($taskGroupCountByEpisode, $cacheIsNotSealEpisodeData);
                    break;
                case 'isSeal':
                    // 封板集数存在
                    $cacheIsNotSealEpisodeData = $taskGroupCountByEpisode;
                    $taskGroupCountByEpisode = array_merge($cacheIsSealEpisodeData, $taskGroupCountByEpisode);
                    break;
            }

            // 复杂查询修正 $taskGroupCountByEpisode 数据
            $taskGroupCountByEpisodeIds = array_column($taskGroupCountByEpisode, null, 'id');
            foreach ($episodesDataList as $episodesDataItem) {
                if (array_key_exists($episodesDataItem['id'], $taskGroupCountByEpisodeIds)) {

                    if ($isComplexTaskQuery) {
                        $episodesDataSortByCode[] = $episodesDataItem;
                    }

                    if ($cacheRangeType === 'None') {
                        if ($episodesDataItem['episode_is_seal'] === 'yes') {
                            $cacheIsSealEpisodeData[] = $taskGroupCountByEpisodeIds[$episodesDataItem['id']];
                        } else {
                            $cacheIsNotSealEpisodeData[] = $taskGroupCountByEpisodeIds[$episodesDataItem['id']];
                        }
                    }
                }
            }

            // 销毁临时变量
            unset($episodesDataList);
            // 已经封板的集数缓存 3600 秒
            Redis::setEx($cacheIsSealEpisodeKey, 3600, json_encode($cacheIsSealEpisodeData));

            // 未封板的集数缓存 100 秒
            Redis::setEx($cacheIsNotSealEpisodeKey, 100, json_encode([
                'episode_count' => $cacheIsNotSealEpisodeData,
                'episode_sort' => $episodesDataSortByCode
            ]));
        }

        // 3.1. 集数数据按集ID字典整理
        $taskGroupCountByEpisodeId = [];
        $offset = 0;
        foreach ($taskGroupCountByEpisode as $taskGroupCountItem) {
            $taskGroupCountByEpisodeId[$taskGroupCountItem['id']] = [
                'start' => $offset,
                'end' => $offset + $taskGroupCountItem['total'],
                'total' => $taskGroupCountItem['total']
            ];
            $offset = $taskGroupCountByEpisodeId[$taskGroupCountItem['id']]['end'];
        }

        $estimatedPageNum = ceil($offset / $page[1]);

        // 3.找到当前分页所在的集数范围
        $pageOffsetStart = ($page[0] - 1) * $page[1];
        if ($page[0] == $estimatedPageNum) {
            $pageOffsetEnd = $offset;
        } else {
            $pageOffsetEnd = $page[0] * $page[1];
        }

        $selectEpisodesIdsRange = [];

        // 3.2 按集数code排序判断集数范围
        $forContinue = false;
        $offsetStart = $pageOffsetStart;
        foreach ($episodesDataSortByCode as $episodesItem) {
            // 获取游标起点
            if (!array_key_exists($episodesItem['id'], $taskGroupCountByEpisodeId)) {
                continue;
            }

            if ($forContinue) {
                if (!in_array($episodesItem['id'], $selectEpisodesIdsRange)) {
                    $selectEpisodesIdsRange[] = $episodesItem['id'];
                }
            }

            if ($pageOffsetStart >= $taskGroupCountByEpisodeId[$episodesItem['id']]['start'] && $pageOffsetStart < $taskGroupCountByEpisodeId[$episodesItem['id']]['end']) {
                // 找到了起点
                $forContinue = true;
                $selectEpisodesIdsRange[] = $episodesItem['id'];
            }

            if (!$forContinue) {
                if ($offsetStart - $taskGroupCountByEpisodeId[$episodesItem['id']]['total'] >= 0) {
                    $offsetStart = $offsetStart - $taskGroupCountByEpisodeId[$episodesItem['id']]['total'];
                }
            }

            // 获取游标结束
            if ($pageOffsetEnd >= $taskGroupCountByEpisodeId[$episodesItem['id']]['start'] && $pageOffsetEnd < $taskGroupCountByEpisodeId[$episodesItem['id']]['end']) {
                if (!in_array($episodesItem['id'], $selectEpisodesIdsRange)) {
                    $selectEpisodesIdsRange[] = $episodesItem['id'];
                }
                break;
            }
        }

        return [
            'episodes_ids' => $selectEpisodesIdsRange,
            'total' => $offset,
            'offset' => $offsetStart
        ];
    }
}
